import 'dart:ui';
import 'dart:math' as math;

import 'package:flutter_widgets_pc/utils/ex/color_ex.dart';

/// 角度弧度扩展
extension DegreeAndRadianEx on double {
  /// 角度转化为弧度
  double get toRadian => this / 180 * math.pi;

  /// 弧度转化为角度
  double get toDegrees => this / math.pi * 180;

  /// 将角度转化为0°~360°范围内
  double get to360Range {
    double angle2 = this;
    while (angle2 < 0) {
      angle2 += 360;
    }
    return angle2 % 360;
  }
}

/// Canvas方法扩展
extension CanvasEx on Canvas {
  /// 绘制底部扇形(3D饼状图底部构成部分)，绘制方向为从下往上绘制。绘制完整的3D扇形图还需要顺序调用[draw3DArcShadow]和[draw3DArcTop]。
  /// 如果绘制多个相连的扇形图，需要先调用所有扇形图的此方法然后才能调用[draw3DArcShadow]和[draw3DArcTop]方法
  ///
  /// [center]为扇形的圆心。[height]为3D扇形的高度。[flipAngle]为翻转角度，0°则和二维饼状图一致，90°则只能看到一个二维矩形.允许取值范围为0°~90°。
  /// [sliceSpace]为各扇形直接的间隙，如果[sweepAngle] >= 360°，则此属性无效，如果动画绘制且最终绘制一个完整的圆，此值请传0，否则可能出现非预期错误。
  /// [offset]为与圆心的偏移值
  void draw3DArcBottom(
      {required Offset center,
      required double radius,
      double innerRadius = 0,
      required double height,
      required double flipAngle,
      required double startAngle,
      required double sweepAngle,
      required Color color,
      Offset offset = Offset.zero,
      double sliceSpace = 0}) {
    if (sweepAngle <= 0 || height <= 0) {
      return;
    }

    Color shadowColor = color.withLightness(0.35);
    var draw = drawArcEx(
        center: center,
        radius: radius,
        flipAngle: flipAngle,
        innerRadius: innerRadius,
        startAngle: startAngle,
        sweepAngle: sweepAngle,
        color: shadowColor,
        offset: offset,
        sliceSpace: sliceSpace);
    if (!draw) {
      return;
    }

    if (sweepAngle < 360) {
      _drawArcSide(
          center: center,
          radius: radius,
          flipAngle: flipAngle,
          innerRadius: innerRadius,
          height: height,
          startAngle: startAngle,
          sweepAngle: sweepAngle,
          color: shadowColor,
          offset: offset,
          sliceSpace: sliceSpace);
    }

    draw3DArcShadow(
        center: center,
        radius: innerRadius,
        flipAngle: flipAngle,
        height: height,
        startAngle: startAngle,
        sweepAngle: sweepAngle,
        color: color.withLightness(0.95),
        offset: offset,
        sliceSpace: sliceSpace,
        drawTop: true);
  }

  /// 绘制3D扇形侧面矩形
  void _drawArcSide(
      {required Offset center,
      required double radius,
      double innerRadius = 0,
      required double height,
      required double flipAngle,
      required double startAngle,
      required double sweepAngle,
      required Color color,
      Offset offset = Offset.zero,
      double sliceSpace = 0}) {
    // 求startAngle过圆心的直线平移sliceSpace / 2f后与圆的交点，得出交点与圆心连线的角度
    var addedDegrees = 90 - math.acos(sliceSpace / 2 / radius).toDegrees;
    // 计算扇形之间的间隙导致中心点偏移的半径
    double offsetRadius = sliceSpace / 2 / math.sin(sweepAngle.toRadian / 2);
    var start = (startAngle + addedDegrees).to360Range;
    var sweep = sweepAngle - addedDegrees * 2;
    var centerX = center.dx + offset.dx;
    var centerY = center.dy + offset.dy * math.cos(flipAngle.toRadian);
    // 椭圆短轴半径
    var radiusShort = radius * math.cos(flipAngle.toRadian);
    double offsetX =
        math.cos((startAngle + sweepAngle / 2).toRadian) * offsetRadius;
    double offsetY = math.sin((startAngle + sweepAngle / 2).toRadian) *
        offsetRadius *
        math.cos(flipAngle.toRadian);

    Paint paint = Paint()
      ..color = color
      ..style = PaintingStyle.fill
      ..strokeWidth = 1;
    if (innerRadius == 0) {
      Path tempPath =
          _createOvalPath(centerX, centerY, radius, radiusShort, start, sweep);
      PathMetric? pathMetric = tempPath.computeMetric();
      if (pathMetric == null || pathMetric.length == 0) {
        return;
      }

      // 计算偏移后的中心点
      centerX += offsetX;
      centerY += offsetY;
      // 绘制开始角度的侧面
      tempPath.reset();
      tempPath.moveTo(centerX, centerY);
      tempPath.lineTo(centerX, centerY - height);
      var tangentForOffset = pathMetric.getTangentForOffset(0);
      if (tangentForOffset == null) {
        return;
      }

      tempPath.lineTo(
          tangentForOffset.position.dx, tangentForOffset.position.dy - height);
      tempPath.lineTo(
          tangentForOffset.position.dx, tangentForOffset.position.dy);
      drawPath(tempPath, paint);

      // 绘制结束角度的侧面
      tempPath.reset();
      tempPath.moveTo(centerX, centerY);
      tempPath.lineTo(centerX, centerY - height);
      tangentForOffset = pathMetric.getTangentForOffset(pathMetric.length);
      if (tangentForOffset == null) {
        return;
      }

      tempPath.lineTo(
          tangentForOffset.position.dx, tangentForOffset.position.dy - height);
      tempPath.lineTo(
          tangentForOffset.position.dx, tangentForOffset.position.dy);
      drawPath(tempPath, paint);
    } else {
      Path tempPath =
          _createOvalPath(centerX, centerY, radius, radiusShort, start, sweep);
      PathMetric? pathMetric = tempPath.computeMetric();
      if (pathMetric == null || pathMetric.length == 0) {
        return;
      }

      var tangentForOffset = pathMetric.getTangentForOffset(0);
      if (tangentForOffset == null) {
        return;
      }

      double x1 = tangentForOffset.position.dx;
      double y1 = tangentForOffset.position.dy;
      tangentForOffset = pathMetric.getTangentForOffset(pathMetric.length);
      if (tangentForOffset == null) {
        return;
      }

      double x2 = tangentForOffset.position.dx;
      double y2 = tangentForOffset.position.dy;

      // 内圆起始点坐标
      double x3;
      double y3;
      // 内圆结束点坐标
      double x4;
      double y4;

      // 求startAngle过圆心的直线平移sliceSpace / 2f后与圆的交点，得出交点与圆心连线的角度
      addedDegrees = 90 - math.acos(sliceSpace / 2 / innerRadius).toDegrees;
      if (addedDegrees * 2 >= sweepAngle) {
        // 说明扇形中心点在内圆半径之外
        x3 = centerX + offsetX;
        y3 = centerY + offsetY;
        x4 = centerX + offsetX;
        y4 = centerY + offsetY;
      } else {
        start = (startAngle + addedDegrees).to360Range;
        sweep = sweepAngle - addedDegrees * 2;
        radiusShort = innerRadius * math.cos(flipAngle.toRadian);
        tempPath = _createOvalPath(
            centerX, centerY, innerRadius, radiusShort, start, sweep);
        pathMetric = tempPath.computeMetric();
        if (pathMetric == null || pathMetric.length == 0) {
          return;
        }

        tangentForOffset = pathMetric.getTangentForOffset(0);
        if (tangentForOffset == null) {
          return;
        }

        x3 = tangentForOffset.position.dx;
        y3 = tangentForOffset.position.dy;
        tangentForOffset = pathMetric.getTangentForOffset(pathMetric.length);
        if (tangentForOffset == null) {
          return;
        }
        x4 = tangentForOffset.position.dx;
        y4 = tangentForOffset.position.dy;
      }

      // 绘制开始角度的侧面
      tempPath.reset();
      tempPath.moveTo(x1, y1);
      tempPath.lineTo(x1, y1 - height);
      tempPath.lineTo(x3, y3 - height);
      tempPath.lineTo(x3, y3);
      drawPath(tempPath, paint);

      // 绘制结束角度的侧面
      tempPath.reset();
      tempPath.moveTo(x2, y2);
      tempPath.lineTo(x2, y2 - height);
      tempPath.lineTo(x4, y4 - height);
      tempPath.lineTo(x4, y4);
      drawPath(tempPath, paint);
    }
  }

  /// 绘制底部扇形与顶部扇形之间的阴影内容(3D饼状图中间构成部分)，绘制方向为从下往上绘制。绘制完整的3D扇形图还需要先调用[draw3DArcBottom]，
  /// 然后在此方法之后调用[draw3DArcTop]。如果绘制多个相连的扇形图，需要先调用所有扇形图的[draw3DArcBottom]方法然后才调用所有扇形图的此方法，
  /// 紧接着调用所有扇形图的[draw3DArcTop]方法
  ///
  /// [center]为扇形的圆心。[height]为3D扇形的高度。[flipAngle]为翻转角度，0°则和二维饼状图一致，90°则只能看到一个二维矩形.允许取值范围为0°~90°。
  /// [sliceSpace]为各扇形直接的间隙，如果[sweepAngle] >= 360°，则此属性无效，如果动画绘制且最终绘制一个完整的圆，此值请传0，否则可能出现非预期错误。
  /// [offset]为与圆心的偏移值。如果[innerRadius] > 0 则绘制3d圆环，[drawTop] == true 表示绘制内环上半部分阴影， == false 表示绘制外环下半部分阴影
  void draw3DArcShadow(
      {required Offset center,
      required double radius,
      required double height,
      required double flipAngle,
      required double startAngle,
      required double sweepAngle,
      required Color color,
      Offset offset = Offset.zero,
      double sliceSpace = 0,
      bool drawTop = false}) {
    if (sweepAngle <= 0 || height <= 0) {
      return;
    }

    double addedDegrees = 0;
    if (sweepAngle < 360) {
      // 求startAngle过圆心的直线平移sliceSpace / 2f后与圆的交点，得出交点与圆心连线的角度
      addedDegrees = 90 - math.acos(sliceSpace / 2 / radius).toDegrees;
      if (addedDegrees * 2 >= sweepAngle) {
        return;
      }

      // 沿着偏移夹角的中轴线进行偏移
      // 计算扇形之间的间隙导致中心点偏移的半径
      double offsetRadius = sliceSpace / 2 / math.sin(sweepAngle.toRadian / 2);
      if (offsetRadius >= radius) {
        // 间隙挤压导致饼状图不可见
        return;
      }
    }

    double start = (startAngle + addedDegrees).to360Range;
    double sweep = sweepAngle - addedDegrees * 2;
    double end = start + sweep;
    double centerX = center.dx + offset.dx;
    double centerY = center.dy + offset.dy * math.cos(flipAngle.toRadian);
    // 椭圆短轴半径
    double radiusShort = radius * math.cos(flipAngle.toRadian);
    // 绘制上下扇形之间的阴影，只需要绘制矩形框即可，视觉上看起就是连贯的
    Paint paint = Paint()
      ..color = color.withLightness(0.35)
      ..style = PaintingStyle.fill
      ..strokeWidth = 1;

    if (start <= 180) {
      if (!drawTop) {
        _drawShadow(paint, centerX, centerY, radius, radiusShort, height, start,
            end > 180 ? 180 - start : end - start);

        if (end > 360) {
          _drawShadow(paint, centerX, centerY, radius, radiusShort, height, 360,
              end - 360);
        }
      }

      if (drawTop) {
        if (end > 180) {
          _drawShadow(paint, centerX, centerY, radius, radiusShort, height, 180,
              end > 360 ? 180 : end - 180);
        }
      }
    } else {
      if (drawTop) {
        _drawShadow(paint, centerX, centerY, radius, radiusShort, height, start,
            end > 360 ? 360 - start : end - start);

        if (end > 540) {
          _drawShadow(paint, centerX, centerY, radius, radiusShort, height, 540,
              end - 540);
        }
      }

      if (!drawTop) {
        if (end > 360) {
          _drawShadow(paint, centerX, centerY, radius, radiusShort, height, 360,
              end > 540 ? 180 : end - 360);
        }
      }
    }
  }

  /// 绘制底部扇形(3D饼状图顶部构成部分)。绘制完整的3D扇形图还需要先调用[draw3DArcBottom]，[draw3DArcShadow]。 如果绘制多个相连的扇形图，
  /// 需要先调用所有扇形图的[draw3DArcShadow]和[draw3DArcTop]方法然后才能调用此方法
  ///
  /// [center]为扇形的圆心。[height]为3D扇形的高度。[flipAngle]为翻转角度，0°则和二维饼状图一致，90°则只能看到一个二维矩形.允许取值范围为0°~90°。
  /// [sliceSpace]为各扇形直接的间隙，如果[sweepAngle] >= 360°，则此属性无效，如果动画绘制且最终绘制一个完整的圆，此值请传0，否则可能出现非预期错误。
  /// [offset]为与圆心的偏移值
  void draw3DArcTop(
      {required Offset center,
      required double radius,
      double innerRadius = 0,
      required double flipAngle,
      required double startAngle,
      required double sweepAngle,
      required Color color,
      Offset offset = Offset.zero,
      double sliceSpace = 0}) {
    if (sweepAngle <= 0) {
      return;
    }

    drawArcEx(
        center: center,
        radius: radius,
        flipAngle: flipAngle,
        innerRadius: innerRadius,
        startAngle: startAngle,
        sweepAngle: sweepAngle,
        color: color,
        offset: offset,
        sliceSpace: sliceSpace);
  }

  /// 绘制扇形阴影
  void _drawShadow(Paint paint, double centerX, double centerY, double radius,
      double radiusShort, double height, double startAngle, double sweepAngle) {
    var path = _createOvalPath(
        centerX, centerY, radius, radiusShort, startAngle, sweepAngle);
    PathMetric? pathMetric = path.computeMetric();
    if (pathMetric == null || pathMetric.length == 0) {
      return;
    }

    var tangentForOffset = pathMetric.getTangentForOffset(0);
    if (tangentForOffset == null) {
      return;
    }

    path.reset();
    path.moveTo(tangentForOffset.position.dx, tangentForOffset.position.dy);
    path.arcTo(
        Rect.fromLTRB(centerX - radius, centerY - radiusShort, centerX + radius,
            centerY + radiusShort),
        startAngle.toRadian,
        sweepAngle.toRadian,
        false);

    tangentForOffset = pathMetric.getTangentForOffset(pathMetric.length);
    if (tangentForOffset == null) {
      return;
    }

    path.lineTo(tangentForOffset.position.dx, tangentForOffset.position.dy - height);
    path.arcTo(
        Rect.fromLTRB(centerX - radius, centerY - radiusShort - height,
            centerX + radius, centerY + radiusShort - height),
        (startAngle + sweepAngle).toRadian,
        (-sweepAngle).toRadian,
        false);

    drawPath(path, paint);
  }

  /// 根据绘制的扇形覆盖角度，创建不同的路径
  Path _createOvalPath(double centerX, double centerY, double radius,
      double radiusShort, double startAngle, double sweepAngle) {
    Path path = Path();
    if (sweepAngle < 360) {
      path.arcTo(
          Rect.fromLTRB(centerX - radius, centerY - radiusShort,
              centerX + radius, centerY + radiusShort),
          startAngle.toRadian,
          sweepAngle.toRadian,
          false);
    } else {
      path.addOval(Rect.fromLTRB(centerX - radius, centerY - radiusShort,
          centerX + radius, centerY + radiusShort));
    }
    return path;
  }

  /// 绘制扇形,有效绘制则返回true
  ///
  /// [center]为扇形的圆心。[radius]指定外圆的半径，[innerRadius]则为内圆半径，[innerRadius] > 0则绘制的是一个圆环。
  /// [startAngle]为扇形起始绘制角度，sweepAngle则为扇形的角度。[offset]指定扇形圆心较[center]的偏移值。
  /// [flipAngle]为扇形沿着过圆心的X轴翻转角度，0°则和二维饼状图一致，90°在二维图像下则只有一条线，三维下是个矩形.允许取值范围为0°~90°。
  /// [sliceSpace]则指定当前扇形与饼状图其它扇形之间的间隙
  bool drawArcEx(
      {required Offset center,
      required double radius,
      double flipAngle = 0,
      double innerRadius = 0,
      required double startAngle,
      required double sweepAngle,
      required Color color,
      Offset offset = Offset.zero,
      double sliceSpace = 0}) {
    if (sweepAngle <= 0) {
      return false;
    }

    double addedDegrees = 0;
    double offsetRadius = 0;
    if (sweepAngle < 360) {
      // 求startAngle过圆心的直线平移sliceSpace / 2f后与圆的交点，得出交点与圆心连线的角度
      addedDegrees = 90 - math.acos(sliceSpace / 2 / radius).toDegrees;
      if (addedDegrees * 2 >= sweepAngle) {
        return false;
      }

      // 沿着偏移夹角的中轴线进行偏移
      // 计算扇形之间的间隙导致中心点偏移的半径
      offsetRadius = sliceSpace / 2 / math.sin(sweepAngle.toRadian / 2);
      if (offsetRadius >= radius) {
        // 间隙挤压导致饼状图不可见
        return false;
      }
    }

    double start = (startAngle + addedDegrees).to360Range.toRadian;
    double sweep = (sweepAngle - addedDegrees * 2).toRadian;
    double radiusShort = radius * math.cos(flipAngle.toRadian);

    double centerX = center.dx + offset.dx;
    double centerY = center.dy + offset.dy * math.cos(flipAngle.toRadian);

    Path tempPath = Path();
    Rect rect = Rect.fromLTRB(centerX - radius, centerY - radiusShort,
        centerX + radius, centerY + radiusShort);
    if (sweepAngle < 360) {
      // 计算偏移后的中心点
      if (innerRadius == 0) {
        tempPath.moveTo(
            centerX +
                math.cos((startAngle + sweepAngle / 2).toRadian) * offsetRadius,
            centerY +
                math.sin((startAngle + sweepAngle / 2).toRadian) *
                    offsetRadius *
                    math.cos(flipAngle.toRadian));
        tempPath.arcTo(rect, start, sweep, false);
      } else {
        Path tempPath2 = Path();
        tempPath2.arcTo(rect, start, sweep, false);
        var computeMetrics = tempPath2.computeMetrics(forceClosed: false);
        var iterator = computeMetrics.iterator;
        if (!iterator.moveNext()) {
          return false;
        }

        PathMetric pathMeasure = iterator.current;
        if (pathMeasure.length > 0) {
          var tangentForOffset = pathMeasure.getTangentForOffset(0);
          if (tangentForOffset != null) {
            tempPath.moveTo(
                tangentForOffset.position.dx, tangentForOffset.position.dy);
            tempPath.arcTo(rect, start, sweep, false);

            // 求startAngle过圆心的直线平移sliceSpace / 2f后与圆的交点，得出交点与圆心连线的角度
            addedDegrees =
                90 - math.acos(sliceSpace / 2 / innerRadius).toDegrees;
            if (addedDegrees * 2 >= sweepAngle) {
              // 说明扇形中心点在内圆半径之外，此时直接绘制扇形即可
              tempPath.lineTo(
                  centerX +
                      math.cos((startAngle + sweepAngle / 2).toRadian) *
                          offsetRadius,
                  centerY +
                      math.sin((startAngle + sweepAngle / 2).toRadian) *
                          offsetRadius *
                          math.cos(flipAngle.toRadian));
            } else {
              start = (startAngle + addedDegrees).to360Range.toRadian;
              sweep = (sweepAngle - addedDegrees * 2).toRadian;
              radiusShort = innerRadius * math.cos(flipAngle.toRadian);

              tempPath2.reset();
              rect = Rect.fromLTRB(centerX - innerRadius, centerY - radiusShort,
                  centerX + innerRadius, centerY + radiusShort);
              tempPath2.arcTo(rect, start, sweep, false);

              var computeMetrics = tempPath2.computeMetrics(forceClosed: false);
              var iterator = computeMetrics.iterator;
              PathMetric? pathMeasure;

              if (!iterator.moveNext()) {
                pathMeasure = null;
              } else {
                pathMeasure = iterator.current;
              }

              if (pathMeasure == null || pathMeasure.length == 0) {
                tempPath.lineTo(
                    centerX +
                        math.cos((startAngle + sweepAngle / 2).toRadian) *
                            offsetRadius,
                    centerY +
                        math.sin((startAngle + sweepAngle / 2).toRadian) *
                            offsetRadius *
                            math.cos(flipAngle.toRadian));
              } else {
                var tangentForOffset =
                    pathMeasure.getTangentForOffset(pathMeasure.length);
                if (tangentForOffset != null) {
                  tempPath.lineTo(tangentForOffset.position.dx,
                      tangentForOffset.position.dy);
                  tempPath.arcTo(rect, start + sweep, -sweep, false);
                }
              }
            }
          }
        }
      }
    } else {
      tempPath.addOval(rect);
      // 填充类型设置为取不想交部分，也就是重叠部分被裁剪
      tempPath.fillType = PathFillType.evenOdd;
      if (innerRadius > 0) {
        radiusShort = innerRadius * math.cos(flipAngle.toRadian);
        rect = Rect.fromLTRB(centerX - innerRadius, centerY - radiusShort,
            centerX + innerRadius, centerY + radiusShort);
        tempPath.addOval(rect);
      }
    }

    Paint paint = Paint();
    paint.isAntiAlias = true;
    paint.color = color;
    paint.style = PaintingStyle.fill;
    drawPath(tempPath, paint);
    return true;
  }
}

extension PathEx on Path {
  /// 获取路径下第一个图型的PathMetric
  PathMetric? computeMetric({bool forceClosed = false}) {
    var computeMetrics = this.computeMetrics(forceClosed: forceClosed);
    var iterator = computeMetrics.iterator;
    if (iterator.moveNext()) {
      return iterator.current;
    }
    return null;
  }
}
